package puzzle.masyu2.solver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import puzzle.common.data.HatanException;
import puzzle.common.point.Direction;
import puzzle.common.point.Pnt;
import puzzle.common.solver.AbstractSolver;
import puzzle.common.solver.BoardSnapshot;
import puzzle.common.solver.SolutionState;
import puzzle.common.solver.Track;
import puzzle.common.solver.TrackTopology;
import puzzle.common.solver.topology.TrackAnalyzer;
import puzzle.masyu2.data.DangoType;
import puzzle.masyu2.data.FlagHelper;
import puzzle.masyu2.data.MasyuBoard;

public class MasyuSolver extends AbstractSolver<MasyuBoard, TrackTopology, MasyuAssumption> {

	public MasyuSolver() {
		addInitialRule(new DangoInitialLocationRule());

		addRule(new BasicLineRule());
		addRule(new DangoRule());

		addTopologicalRule(new AviodLoopTopologicalRule());
		addTopologicalRule(new ClosureTopologicalRule());
	}

	@Override
	protected TrackTopology analyzeTopology(MasyuBoard b) {
		MasyuTrackAnalyzable g = new MasyuTrackAnalyzable(b);
		TrackAnalyzer z = new TrackAnalyzer();
		return z.analyze(g);
	}

	@Override
	protected SolutionState checkSolutionState(MasyuBoard b, TrackTopology topology) {
		List<Track> tracks = topology.getTracks();
		boolean isComplete = true;

		if (tracks.size() > 1) {
			for (Track track : tracks) {
				if (!track.isOpen()) {
					return SolutionState.HATAN;
				}
			}
			isComplete = false;
		} else if (tracks.size() == 1) {
			if (tracks.get(0).isOpen()) {
				isComplete = false;
			}
		}

		for (int y = 0; y < b.height; y++) {
			for (int x = 0; x < b.width; x++) {
				switch (b.getLineCountAround(x, y)) {
				case 0:
					if (b.getDangoType(x, y) != DangoType.NONE) {
						isComplete = false;
					}
					break;
				case 1:
					if (b.getForbiddenCountAround(x, y) == 3) {
						isComplete = false;
					}
					break;
				case 2:
					int f = b.getExistFlags(x, y);
					switch (b.getDangoType(x, y)) {
					case SHIRO:
						if (f != 0x05 && f != 0x0a) {
							return SolutionState.HATAN;
						}
						break;
					case KURO:
						if (f != 0x03 && f != 0x06 && f != 0x0c && f != 0x09) {
							return SolutionState.HATAN;
						}
						break;
					}
					break;
				case 3:
				case 4:
					return SolutionState.HATAN;
				}
			}
		}
		return isComplete ? SolutionState.COMPLETE : SolutionState.INCOMPLETE;
	}

	@Override
	protected List<MasyuAssumption> assume(MasyuBoard b0, TrackTopology topology,
			MasyuAssumption prevAssumption) {
		List<Track> tracks = topology.getTracks();
		List<MasyuAssumption> as = new ArrayList<MasyuAssumption>();
		for (Track track : tracks) {
			assumeAux(as, b0, track.getEnds().car);
			assumeAux(as, b0, track.getEnds().cdr);
		}
		return as;
	}

	private void assumeAux(List<MasyuAssumption> as, MasyuBoard b0, Pnt p) {
		int candidateFlags = ~(b0.getExistFlags(p.x, p.y) | b0.getForbiddenFlags(p.x, p.y));
		for (Direction d : FlagHelper.getDirectionsForFlags(candidateFlags)) {
			as.add(new MasyuAssumption(p, d));
		}
	}

	@Override
	protected void applyAssumption(
			List<BoardSnapshot<MasyuBoard, TrackTopology, MasyuAssumption>> bss, MasyuBoard b,
			MasyuAssumption a) throws HatanException {
		b.setExistFlag(a.p.x, a.p.y, a.d);
		b.setAssumed(a.p.x, a.p.y, a.d);
	}

	@Override
	protected void excludeAssumption(MasyuBoard b0, MasyuAssumption a) throws HatanException {
		b0.setForbiddenFlag(a.p.x, a.p.y, a.d);
	}

	@Override
	protected boolean isAlreadyTried(
			List<BoardSnapshot<MasyuBoard, TrackTopology, MasyuAssumption>> bss, MasyuBoard b0,
			MasyuAssumption a) {
		if (b0.getExistFlag(a.p.x, a.p.y, a.d) || b0.getForbiddenFlag(a.p.x, a.p.y, a.d)) {
			return true;
		}

		for (BoardSnapshot<MasyuBoard, TrackTopology, MasyuAssumption> bs : bss) {
			MasyuBoard b = bs.getBoard();
			if (b.getExistFlag(a.p.x, a.p.y, a.d)) {
				return true;
			}
		}
		return false;
	}

	@Override
	protected void sortBoardSnapshots(
			List<BoardSnapshot<MasyuBoard, TrackTopology, MasyuAssumption>> bss) {
		Collections.sort(bss, TrackTopology.TOTAL_LEG_BOARD_COMPARATOR);
	}

}
